package model.building;

import java.util.List;
import java.util.LinkedList;
import java.util.Observer;
import java.util.Observable;

import model.City;
import model.Ressources;
import model.util.Vector3D;
import model.util.Object3D;

public abstract class Building extends Observable {
	
    /** Attributs */
    private City m_city;
    private Object3D m_object3d;
    private Activity m_activity;
    private List<IllegalActivity> m_illegalActivityList;

    /** Constructeurs */
    public Building(City city, Object3D object3d, Activity activity) {
        m_city = city;
        m_object3d = object3d;
        m_activity = activity;
        m_illegalActivityList = new LinkedList<IllegalActivity>();
    }

    /** Accesseurs */
    public City getCity() { return m_city; }
    public Object3D getObject3D() { return m_object3d; }
    public Activity getActivity() { return m_activity; }
    public List<IllegalActivity> getIllegalActivities() { return m_illegalActivityList; }

    public abstract String getName();
    public abstract double getMaintainCost();
    public abstract double getBaseDestructionCost();
    public final double getDestructionCost() {
        double destructionCost = getBaseDestructionCost();
        for (IllegalActivity illegalActivity : m_illegalActivityList) 
            destructionCost += illegalActivity.getDestructionCost();
        return destructionCost;
    }

    /** Predicats */
    public boolean intersects(Building building) {
        return getObject3D().intersects(building.getObject3D());
    }

    /** Methodes */
    public void construct() { m_activity.construct(getCity().getEconomy()); }
    public void update(double deltaTps) {
        getCity().getEconomy().remove(Ressources.Type.DOLLARS, getMaintainCost() * deltaTps);
        m_activity.update(deltaTps, getCity().getEconomy());
        for (IllegalActivity illegalActivity : m_illegalActivityList) 
            illegalActivity.update(deltaTps, getCity().getEconomy());
    }
    public void destroy() {
        for (IllegalActivity illegalActivity : m_illegalActivityList)
            illegalActivity.destroy(getCity().getEconomy());
        m_activity.destroy(getCity().getEconomy());
        getCity().getEconomy().remove(Ressources.Type.DOLLARS, getDestructionCost());
    }

    public void add(IllegalActivity illegalActivity) { m_illegalActivityList.add(illegalActivity); }
    public void remove(IllegalActivity illegalActivity) { m_illegalActivityList.remove(illegalActivity); }

    /** Classe internes */
    public static abstract class Activity extends Observable {

        /** Attributs */
        private Ressources m_oldImpact;
        
        /** Constructeurs */
        public Activity() {
            m_oldImpact = new Ressources();
        }
        
        public Activity(Activity activity) {
            m_oldImpact = new Ressources();
        }
        
        /** Predicats */
        public boolean canConstruct(Ressources economy) { return true; }

        /** Methodes */
        public abstract void construct(Ressources economy);
        public Ressources getImpact(double deltaTps, Ressources economy) {
            
            // TODO: remove
            return new Ressources();
        }
        public abstract void destroy(Ressources economy);

        protected void applyImpact(Ressources impact, Ressources economy) {
            for (Ressources.Type t : impact.types()) economy.add(t, impact.getQuantity(t));
        }

        public void update(double deltaTps, Ressources economy) {
            Ressources newImpact = getImpact(deltaTps, economy);
            applyImpact(newImpact, economy);
            if (!m_oldImpact.isEquals(newImpact)) {
                m_oldImpact = newImpact;
                setChanged();
                notifyObservers(newImpact);
            }
        }
    }

    public class IllegalActivity extends Activity {
        
        /** Attributs */
        double m_incomes;

        /** Constructeurs */
        public IllegalActivity() { m_incomes = 0; }

        /** Accesseurs */
        public double getDestructionCost() { return m_incomes/10; }

        /** Methodes */
        public void construct(Ressources economy) {}
        public Ressources getImpact(double deltaTps, Ressources economy) {
            double goodsUsed = 5.0 * deltaTps;
            double dollarsCreated = goodsUsed;
            Ressources ret = new Ressources();
            ret.remove(Ressources.Type.GOODS,goodsUsed);
            ret.add(Ressources.Type.DOLLARS, dollarsCreated);
            m_incomes += dollarsCreated;

            return ret;
        }
        public void destroy(Ressources economy) {
            economy.remove(Ressources.Type.DOLLARS, m_incomes/10);
        }
    }
}